
/**
 * @public
 * @constructor
 */
function GameLoop(finiteStateMachine, gameEntitiesLayer0, gameEntitiesLayer1, inputQueue) {
	
	window.requestAnimationFrame = 
			window.requestAnimationFrame || /* Firefox 23 / IE 10 / Chrome */
			window.mozRequestAnimationFrame || /* Firefox < 23 */
			window.webkitRequestAnimationFrame || /* Safari */
			window.msRequestAnimationFrame || /* IE  */
			window.oRequestAnimationFrame; /* Opera */
	
	if(!window.requestAnimationFrame) {
		throw new Exception("Failed to get requestAnimationFrame funtion");
	}
	
	this.finiteStateMachine = finiteStateMachine;
	this.gameEntitiesLayer0 = gameEntitiesLayer0;
	this.gameEntitiesLayer1 = gameEntitiesLayer1;
	this.inputQueue = inputQueue;

	this.canvas = document.getElementById('canvas');
	this.context = this.canvas.getContext('2d');
	
	this.lastLoopCallTime = 0;
	this.accumulatedTimeMs = 0;
	
	/**
	 * @public
	 */
	this.start = function(canvasWidth, canvasHeight) {
		this.canvas.width=canvasWidth;
		this.canvas.height=canvasHeight;
		this.lastLoopCallTime = this.getCurrentTimeMs();
		this.update();
	};
	
	/**
	 * @private
	 */
	this.update = function() {
		var self = this;
		
		var actualLoopDurationMs = self.getCurrentTimeMs()-self.lastLoopCallTime;
		self.lastLoopCallTime = self.getCurrentTimeMs();
		self.accumulatedTimeMs += actualLoopDurationMs;
		while(self.accumulatedTimeMs>=FIXED_STEP_IDEAL_DURATION_MS) {
			self.updateState();
			self.accumulatedTimeMs -= FIXED_STEP_IDEAL_DURATION_MS;
		}
		
		self.updateGraphics();
		
		window.requestAnimationFrame(function() {self.update();});
	};
	
	/**
	 * @private
	 */
	this.processInput = function() {
		while(!inputQueue.isEmpty()) {
			var event = inputQueue.pop();
			this.finiteStateMachine.onProcessInput(event);
						
			for(var i=0; i<this.gameEntitiesLayer0.length; i++) {
				var gameEntity = this.gameEntitiesLayer0[i];
				gameEntity.processInput(event);
			}
			for(var i=0; i<this.gameEntitiesLayer1.length; i++) {
				var gameEntity = this.gameEntitiesLayer1[i];
				gameEntity.processInput(event);
			}
		}
	};
	
	/**
	 * @private
	 */
	this.updateState = function() {		
		this.processInput();
		
		this.finiteStateMachine.onUpdateState();
		
		for(var i=0; i<this.gameEntitiesLayer0.length; i++) {
			var gameEntity = this.gameEntitiesLayer0[i];
			gameEntity.updateState();
		}
		for(var i=0; i<this.gameEntitiesLayer1.length; i++) {
			var gameEntity = this.gameEntitiesLayer1[i];
			gameEntity.updateState();
		}		
	};
	
	/**
	 * @private
	 */
	this.updateGraphics = function() {
		this.context.fillStyle = "white";
		this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
		
		for(var i=0; i<this.gameEntitiesLayer0.length; i++) {
			var gameEntity = this.gameEntitiesLayer0[i];
			gameEntity.updateGraphics(this.context);
		}
		for(var i=0; i<this.gameEntitiesLayer1.length; i++) {
			var gameEntity = this.gameEntitiesLayer1[i];
			gameEntity.updateGraphics(this.context);
		}
	};	
	
	/**
	 * @private
	 */
	this.getCurrentTimeMs = function() {
		return new Date().getTime();
	};

};
